/**
 * @file datagramBuffer.c
 * @brief Implementación de las funciones del modulo auxiliar datagramBuffer.
 *
 * Fichero que contiene el cuerpo de las funciones de gestion de buffer de almacenamiento de datagramas.
*/
 
#include "datagramBuffer.h"


struct _datagramBuffer{
	MSG_DATA *data;					/*!< Array de mensajes encolados*/
	int head;						/*!< Cabeza del buffer circular -> Apunta a la siguiente posicion a insertar*/
	int tail;						/*!< Cola del buffer circular -> Apunta al ultimo dato insertado*/
	int iterator;					/*!< Iterador interno, no robusto -> Apunta al siguiente dato a mostrar, -1 si no inicializado*/
	size_t size;					/*!< Tamanno del buffer circular*/
};

DATAGRAM_BUFFER iniDatagramBuffer(size_t bandwidth)
{
	int i=0;
	
	DATAGRAM_BUFFER dataBuffer=NULL;
	
	/*CdE*/
	if(bandwidth<0){
		return NULL;
	}
	
	dataBuffer=(struct _datagramBuffer *) calloc(1, sizeof(struct _datagramBuffer));
	if(dataBuffer==NULL){
		return NULL;
	}

	dataBuffer->size=bandwidth+1;													/*Tamanno del buffer = BANDWIDTH + 1 -> Permite distinguir estado de vacio y lleno*/
	
	dataBuffer->data=(MSG_DATA *)calloc(dataBuffer->size, sizeof(MSG_DATA));
	if(dataBuffer->data==NULL){
		free(dataBuffer);
		return NULL;
	}
	
	/*Inicializacion del buffer circular de mensajes*/
	for(i=0; i<dataBuffer->size; i++){
		dataBuffer->data[i]=iniMsgData();
		if(dataBuffer->data[i]==NULL){
			for(i--;i<=0; i--){
				freeMsgData(dataBuffer->data[i]);
			}
			
			free(dataBuffer);
			return NULL;
		}
	}
	
	/*Inicializacion de punteros*/
	dataBuffer->head=0;
	dataBuffer->tail=0;
	dataBuffer->iterator=-1;

	
	return dataBuffer;
}

ERR_CODE cleanEntryDatagramBuffer(DATAGRAM_BUFFER buff)
{
	if(buff==NULL || buff->data==NULL || buff->head==buff->tail){
		return ERR;
	}

	cleanMsgData(buff->data[buff->tail]);

	buff->tail=(buff->tail+1)%(buff->size);				/*Incremento de la cola*/
	
	return OK;
}

ERR_CODE cleanIntervalDataDatagramBuffer(DATAGRAM_BUFFER buff, int last)
{
	if(buff==NULL || buff->data==NULL){
		return ERR;
	}

	while(buff->tail!=last){
		cleanEntryDatagramBuffer(buff);
	}

	return OK;
}

void cleanDatagramBuffer(DATAGRAM_BUFFER buff)
{
	int i=0;

	if(buff==NULL || buff->data==NULL){
		return;
	}
		
	for(i=0; i<buff->size; i++){
		cleanEntryDatagramBuffer(buff);
	}

	/*Inicializacion de punteros*/
	buff->head=0;
	buff->tail=0;
	
	return;
}

void freeDatagramBuffer(DATAGRAM_BUFFER buff)
{
	int i=0;
	
	if(buff==NULL){
		return;
	}
	
	for(i=0; i<buff->size; i++){
		freeMsgData(buff->data[i]);
	}


	free(buff->data);
	free(buff);
	
	return;
}

ERR_CODE insertDatagramBuffer(DATAGRAM_BUFFER buff, BYTE *datagram, int datagramSize)
{	
	if(buff==NULL || buff->data==NULL || datagram==NULL){
		return ERR;
	}
	
	if(isFullDatagramBuffer(buff)){					/* Buffer lleno*/
		return FULL;
	}
	
	if(insertMsgData(buff->data[buff->head], datagram, datagramSize)!=ETH_OK){
		return ERR;
	}
	
	buff->head=(buff->head+1)%(buff->size);			/* Incremento de la cabeza*/
	
	return OK;
}

ERR_CODE undo(DATAGRAM_BUFFER buff)
{
	if(buff==NULL || buff->data==NULL){
		return ERR;
	}
	
	if(cleanEntryDatagramBuffer(buff)!=OK){
		return ERR;
	}
	
	buff->head=buff->head>=1? buff->head-1: buff->size-1;			/* Decremento de la cabeza -> head = (head-1) mod(size)*/
																	/* Nota: En C, la operacion modulo permite resultados negativos. Para solventarlo, se implementa explicito*/
	
	return OK;
}

int getHeadDatagramBuffer(DATAGRAM_BUFFER buff)
{
	return buff->head;
}

int getTailDatagramBuffer(DATAGRAM_BUFFER buff)
{
	return buff->tail;
}

size_t getTotalSizeDatagramBuffer(DATAGRAM_BUFFER buff)
{
	return buff->size;
}

size_t getSizeDatagramBuffer(DATAGRAM_BUFFER buff)
{
	if(buff->head>=buff->tail){				/* Buffer lineal*/
		return buff->head-buff->tail;
	} else{									/* Buffer invertido*/
		return buff->size-buff->tail+buff->head;
	}
}

short isFullDatagramBuffer(DATAGRAM_BUFFER buff)
{
	if(buff->tail>=1 && buff->head==buff->tail-1){			/* Buffer lineal*/
		return 1;
	}
	else if(buff->tail==0 && buff->tail==buff->size-1){		/* Buffer invertido*/
		return 1;
	} else{
		return 0;
	}
}

short isEmptyDatagramBuffer(DATAGRAM_BUFFER buff)
{
	return buff->head==buff->tail;
}

void restartIteratorDatagramBuffer(DATAGRAM_BUFFER buff)
{
	if(buff==NULL){
		return;
	}
	
	buff->iterator=buff->tail;
}

ITERATION_STATE iterateDatagramBuffer(DATAGRAM_BUFFER buff, MSG_DATA dst)
{
	if(buff==NULL || buff->data==NULL || buff->iterator==-1){
		return ITERATION_ERR;
	}
	
	
	if(isEmptyDatagramBuffer(buff) || buff->iterator==buff->head){			/* Buffer vacio o iterador ha alcanzado el final -> FIN DE ITERACION */
		buff->iterator=-1;
		return ITERATION_END;
	}

	if(cpyMsgData(dst, buff->data[buff->iterator])!=OK){					/* Extraccion del mensaje*/
		return ITERATION_ERR;
	}

	buff->iterator=(buff->iterator+1)%(buff->size);							/* Incremento del iterador*/
	
	return ITERATION_OK;
}

ERR_CODE extractDatagramBuffer(DATAGRAM_BUFFER buff, MSG_DATA dst)
{
	if(buff==NULL ||buff->data==NULL){
		return ERR;
	}
	
	if(isEmptyDatagramBuffer(buff)){										/* Buffer vacio -> Imposible extraer*/
		return EMPTY;
	}
	
	/* Extraccion del mensaje */
	if(cpyMsgData(dst, buff->data[buff->tail])!=OK){
		return ERR;
	}
	
	cleanMsgData(buff->data[buff->tail]);

	buff->tail=(buff->tail+1)%buff->size;									/* Incremento de la cola*/
	
	return OK;
}

